/*	
 *				
 *					ISNW ---- It is a war.It is not warcraft!
 *
 *
 *					Author:					zhoukeli(haxixi_keli)
 *					Filename:				vector2d.h
 *					CreateTime:				2008/12/08
 *					ModifyTime:				2008/12/08
 *					ModifyTime:				2008/12/09
 *
 */

#ifndef __ISNW_VECTOR_2D_H__
#define __ISNW_VECTOR_2D_H__

#include "isnwTypes.h"
#include "isnwMath.h"

namespace ISNW
{
namespace core
{
	template<typename T>
	class vector2d
	{
	public:
		vector2d():x(0),y(0){}
		vector2d(const T& tx,const T& ty):x(tx),y(ty){}
		vector2d(const vector2d<T>& other):x(other.x),y(other.y){}

	public:
		vector2d<T> operator - ()const{return vector2d<T>(-x,-y);}

		vector2d<T>& operator = (const vector2d<T>& other){x = other.x;y = other.y;return *this;}

		T& operator [](u32 idx)
		{
			if(idx == 0) return x;
			if(idx == 1) return y;

			return x;
		}

		const T& operator [] (u32 idx) const
		{
			if(idx == 0) return x;
			if(idx == 1) return y;

			return x;
		}

		vector2d<T>& operator += (const vector2d<T>& other){x += other.x;y += other.y;return *this;}
		vector2d<T>& operator += (const T& value){x += value;y += value;return *this;}

		vector2d<T> operator + (const vector2d<T>& other)const{return vector2d<T>(x+other.x,y+other.y);}
		vector2d<T> operator + (const T& value)const{return vector2d<T>(x+value,y+value);}

		vector2d<T>& operator -= (const vector2d<T>& other){x -= other.x;y -= other.y;return *this;}
		vector2d<T>& operator -= (const T& value){x -= value;y -= value;return *this;}

		vector2d<T> operator - (const vector2d<T>& other)const{return vector2d<T>(x-other.x,y-other.y);}
		vector2d<T> operator - (const T& value)const{return vector2d<T>(x-value,y-value);}

		vector2d<T>& operator *= (const T& scale){x *= scale;y *= scale;return *this;}
		vector2d<T>& operator *= (const vector2d<T>& other){x *= other.x;y *= other.y;return *this;}

		vector2d<T> operator * (const T& scale)const{return vector2d<T>(x*scale,y*scale);}
		vector2d<T> operator * (const vector2d<T>& other)const{return vector2d<T>(x*other.x,y*other.y);}

		vector2d<T>& operator /= (const T& scale)
		{
			if(scale)
			{
				x /= scale;
				y /= scale;
			}
			return *this;
		}

		vector2d<T>& operator /= (const vector2d<T>& other)
		{
			if(other.x)x /= other.x;
			if(other.y)y /= other.y;
			
			return *this;
		}

		vector2d<T> operator / (const T& scale)const
		{
			if(scale)
				return vector2d<T>(x*scale,y*scale);

			return *this;
		}

		vector2d<T> operator / (const vector2d<T>& other)const
		{
			if(other.x && other.y)
			{
				return vector2d<T>(x/other.x,y/other.y);
			}
			return *this;
		}

		bool operator <= (const vector2d<T>& other)const{return (x <= other.x && y <= other.y);}
		bool operator >= (const vector2d<T>& other)const{return (x >= other.x && y >= other.y);}

		bool operator < (const vector2d<T>& other)const{return (x < other.x && y < other.y);}
		bool operator > (const vector2d<T>& other)const{return (x > other.x && y > other.y);}

		bool operator == (const vector2d<T>& other)const{return (x == other.x && y == other.y);}
		bool operator != (const vector2d<T>& other)const{return !(*this == other);}

		bool equal(const vector2d<T>& other)const
		{
			return (core::_equal_(x,other.x) && core::_equal_(y,other.y));
		}

		bool set(const T& _x,const T& _y){x = _x;y= _y;}
		bool set(const vector2d<T>& other){x = other.x;y = other.y;}

		T getlength()const{return (T)core::_sqrt_((f32)(x*x + y*y));}
		T getlengthSQ()const{return (x*x+y*y);}

		T dotproduct(const vector2d<T>& other)const{return (x*other.x + y*other.y);}

		T getdistancefrom(const vector2d<T>& other)const{return (vector2d<T>(*this-other)).getlength();}
		T getdistancefromSQ(const vector2d<T>& other)const{return (vector2d<T>(*this-other)).getlengthSQ();}

		vector2d<T>& rotateby(const T& angle,const vector2d<T>& center)
		{
			f32 sn = core::_sin_((f32)angle);
			f32 cs = core::_cos_((f32)angle);
			
			vector2d<T> tmp = *this - center;

			x = tmp.x * cs - tmp.y * sn;
			y = tmp.x * sn + tmp.y * cs;

			*this += center;
		}

		vector2d<T>& normalize()
		{
			f32 l = x * x + y * y;
			if(l == 0)
				return *this;
			l = core::_sqrt_(l);

			x /= l;
			y /= l;

			return *this;
		}

		T& getangle()const
		{
			if(x == 0)
			{
				if(y > 0)
					return (T)90;
				else if(y < 0)
					return (T)270;
				else 
					return (T)0;
			}
			if(y == 0)
			{
				if(x > 0)
					return (T)0;
				else if(x < 0)
					return (T)180;
				else 
					return (T)0;
			}

			if(y > 0)
				return (T)core::_arctan_(y/x);

			return (T)(180 + core::_arctan_(y/x));
		}

		//! this <-> other
		T& getanglewith(const vector2d<T>& other)const
		{
			f32 ab = dotproduct(other);
			f32 ablen = getlength()*other.getlength();
			if(ablen == 0)
				return 0;
			return (T)core::_arccos_(ab/ablen);
		}

	public:
		T x;
		T y;
	};

	template<typename T>
	vector2d<T> operator * (const T& scale,const vector2d<T>& vec)
	{
		return vec * scale;
	}

	//! typedef for float vector
	typedef vector2d<f32> vector2df;
	//! typedef for int vector
	typedef vector2d<s32> vector2di;

} // end namespace core
} // end namespace ISNw
#endif